#include "pch.h"
#include "GDxGpuProfiler.h"


GDxGpuProfiler& GDxGpuProfiler::Get()
{
	static GDxGpuProfiler* instance = new GDxGpuProfiler();
	return *instance;
}

void GDxGpuProfiler::Initialize(ID3D11Device* pDevice, ID3D11DeviceContext* pContext,int num)
{
	
	D3D11_QUERY_DESC query_disjoint_description = {};
	query_disjoint_description.Query = D3D11_QUERY_TIMESTAMP_DISJOINT;
	D3D11_QUERY_DESC query_timestamp_description = {};
	query_timestamp_description.Query = D3D11_QUERY_TIMESTAMP;

	ID3D11Query* query_disjoint;
	pDevice->CreateQuery(&query_disjoint_description, &query_disjoint);
	mQD = query_disjoint;

	mQTSs.resize(num * 2); 
	std::generate(mQTSs.begin(), mQTSs.end(),[&pDevice,&query_timestamp_description] {
		ID3D11Query* query;
		pDevice->CreateQuery(&query_timestamp_description, &query);
		return query;
		});
	
	mpContext = pContext;
}

void GDxGpuProfiler::BeginFrame()
{
	mProfileDatas.clear();
	mProfileMap.clear();

	mpContext->Begin(mQD.Get());
}

void GDxGpuProfiler::EndFrame()
{
	/*mpContext->End((mQTSs)->Get());*/
	mpContext->End(mQD.Get());

	while (mpContext->GetData(mQD.Get(), NULL, 0, 0) == S_FALSE)
	{
		Sleep(1);       // Wait a bit, but give other threads a chance to run
	}

	D3D11_QUERY_DATA_TIMESTAMP_DISJOINT tsDisjoint;
	mpContext->GetData(mQD.Get(), &tsDisjoint, sizeof(tsDisjoint), 0);
	if (tsDisjoint.Disjoint)
	{
		return;			// 中间发生了些问题，该时间记录不可靠
	}
	
	mSumPass = 0;
	for (size_t i = 0; i < mProfileDatas.size() ; i++)
	{
		size_t bi = 2 * i;
		size_t ei = bi + 1;

		UINT64 tsStart, tsEnd;
		HRESULT hr1 = S_FALSE,hr2 = S_FALSE;
		do 
		{
			if (hr1 == S_FALSE)
			{
			hr1 = mpContext->GetData(mQTSs[bi].Get(), &tsStart, sizeof(UINT64), 0);
			}
			if (hr2 == S_FALSE)
			{
			hr2 = mpContext->GetData(mQTSs[ei].Get(), &tsEnd, sizeof(UINT64), 0);
			}
		} while((hr1 | hr2) == S_FALSE);
		

		mProfileDatas[i].time = double(tsEnd - tsStart) /
			double(tsDisjoint.Frequency) * 1000.0;
		mSumPass += mProfileDatas[i].time;
	}
}

void GDxGpuProfiler::StartGpuProfile(const std::string& profileName)
{

	mProfileDatas.push_back({ profileName });
	auto ci = mProfileDatas.size() - 1;
	mProfileMap.insert({ profileName,ci });

	mpContext->End(mQTSs[ci * 2 ].Get());
}

void GDxGpuProfiler::EndGpuProfile(const std::string& profileName)
{
	if (mProfileMap.find(profileName) == mProfileMap.end())
	{
		throw "Call StartGpuProfile first";
		return;
	}

	mpContext->End(mQTSs[mProfileMap[profileName] * 2 + 1].Get());
}

const std::vector<ProfileData>& GDxGpuProfiler::GetProfileDatas()
{
	return mProfileDatas;
}
